package edu.pku.hawk.controller.detectmonitor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import edu.pku.hawk.controller.util.Config;
import edu.pku.hawk.controller.util.Constants;
import edu.pku.hawk.controller.util.DetectorInfo;
import edu.pku.hawk.controller.util.MProducerQueue;
import edu.pku.hawk.controller.util.MQManager;
import edu.pku.hawk.controller.util.ProcDes;
import edu.pku.hawk.controller.util.RegionPath;
import edu.pku.hawk.controller.util.RegionPoint;
import edu.pku.hawk.controller.util.ReportQueue;
import edu.pku.hawk.controller.util.SensorAllData;
import edu.pku.hawk.controller.util.SerResource;
import edu.pku.hawk.controller.util.SerResourceList;
import edu.pku.hawk.controller.util.ServiceManager;
import edu.pku.hawk.controller.util.SubjectDescription;
import edu.pku.hawk.controller.util.Util;
import edu.pku.hawk.service.bo.FusionResult;
import edu.pku.hawk.service.bo.FusionSensorData;
import edu.pku.hawk.service.bo.RowSensorData;

public class CombinedDetectionMonitor {

	private MQManager mgr;
	private AnalyzeResult analyzeResult;
	private ServiceManager serMgr;
	private ProcDes procDes;

	HashMap<Integer, ArrayList<FusionSensorData>> fusiontypeset = new HashMap<Integer, ArrayList<FusionSensorData>>();

	public CombinedDetectionMonitor() throws Exception {

		Config.Singleton.Instance.initialize();

		System.out.print(Constants.head() + "Connecting to Message Queue...");
		mgr = new MQManager(Constants.MQServer_Name, Constants.MQServer_Password, Constants.MQServer_URL);
		System.out.println("Down.");
		mgr.createProducerQueue(Constants.Ctrl_UI_Sensor_QueueName);
		mgr.createProducerQueue(Constants.Ctrl_UI_Path_QueueName);
		mgr.createProducerQueue(Constants.Ctrl_UI_Battlefield_QueueName);
		mgr.createConsumerQueue(Constants.Ctrl_UI_Proc_QueueName);
		mgr.createProducerQueue(Constants.Ctrl_UI_ProcConfirm_QueueName);
		mgr.createConsumerQueue(Constants.Ctrl_Cloud_RscResponse_QueueName);
		mgr.createProducerQueue(Constants.Ctrl_UI_ClearSandResponse_QueueName);
		mgr.createConsumerQueue(Constants.Ctrl_UI_ClearSandRequest_QueueName);

		System.out.println(Constants.head() + "Connecting to service manager...");
		serMgr = new ServiceManager(mgr);
		System.out.println("Down.");

		// mgr.createProducerQueue(Constants.Figure_Monitor_Client_QueueName);
		// mgr.createProducerQueue(Constants.Infrared_Monitor_Client_QueueName);
		// mgr.createProducerQueue(Constants.Metal_Monitor_Client_QueueName);
		// mgr.createProducerQueue(Constants.Pressure_Monitor_Client_QueueName);
		// mgr.createProducerQueue(Constants.Smoke_Monitor_Client_QueueName);
		// mgr.createProducerQueue(Constants.Sound_Monitor_Client_QueueName);
		//
		// mgr.createConsumerQueue(Constants.Infrared_ROW_QueueName);
		// mgr.createConsumerQueue(Constants.Metal_ROW_QueueName);
		// mgr.createConsumerQueue(Constants.Pressure_ROW_QueueName);
		// mgr.createConsumerQueue(Constants.Smoke_ROW_QueueName);
		// mgr.createConsumerQueue(Constants.Sound_ROW_QueueName);
		// mgr.createConsumerQueue(Constants.Figure_ROW_QueueName);

		// mgr.createProducerQueue(Constants.Figure_Monitor_Process_QueueName);
		// mgr.createProducerQueue(Constants.Infrared_Monitor_Process_QueueName);
		// mgr.createProducerQueue(Constants.Metal_Monitor_Process_QueueName);
		// mgr.createProducerQueue(Constants.Pressure_Monitor_Process_QueueName);
		// mgr.createProducerQueue(Constants.Smoke_Monitor_Process_QueueName);
		// mgr.createProducerQueue(Constants.Sound_Monitor_Process_QueueName);

		// mgr.createConsumerQueue(Constants.ListenAvailable_Process_Monitor_QueueName);

		analyzeResult = new AnalyzeResult();
		procDes = ProcDes.EmptyProcess();
		// procDes = ProcDes.combinationProcess();

		System.out.println(Constants.head() + "Start monitor.");
	}

	public void run() {

		System.out.println("Controller started.");

		for (int i = 0; i < 15; i++) {
			sendBattlefieldToClient();
			sendServiceProfileToClient();
		}

		if (Config.UsingTimerOrSleep == true) {

			Timer timer = new Timer();
			TimerTask timerTask = new TimerTask() {
				public void run() {
					singleLoop();
				}
			};
			timer.schedule(timerTask, 3000, Config.MonitorLoopInterval);

		} else {
			//
			while (true) {
				try {
					Thread.sleep(Config.MonitorLoopInterval);
					singleLoop();
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
	}

	private long timeCount = System.currentTimeMillis();

	private int clientSendCount = 0;

	public void singleLoop() {

		long t = System.currentTimeMillis();

		// Clear sand command
		receiveClearSandCmd();

		// Receive process customization
		receiveProcessCustomization();

		// Fetch detector data from DB and analyze it.
		detectAnalysis();

		if (clientSendCount >= Config.MonitorBuffer - 1) {

			// Fusion
			fusionAnalyze();

			// Send single analyzeResult to client
			sendSensorDataToClient();

			// Send fusion path to client
			sendPathToClient();

			clientSendCount = 0;
			analyzeResult.clearReportMapById();
			analyzeResult.clearReportMapQueue();
		}

		clientSendCount++;

		System.gc();

		long currt = System.currentTimeMillis();
		Util.logln(Constants.head() + "([loop time count]: " + (currt - t) + ":" + (currt - timeCount) + "ms)");
		if (currt - timeCount > 3000) {
			Util.logln("__________________________________!!!!!!!!!!!!!!!!!!!!!!|");
			Util.logln("__________________________________!!!!!!!!!!!!!!!!!!!!!!|");
			Util.logln("__________________________________!!!!!!!!!!!!!!!!!!!!!!|");
		} else if (currt - t > 3000) {
			Util.logln("__________________________________??????????????????????|");
			Util.logln("__________________________________??????????????????????|");
			Util.logln("__________________________________??????????????????????|");
		} else {
			Util.logln("__________________________________|");
		}
		timeCount = currt;
	}

	public void receiveClearSandCmd() {
		try {
			String cmd = mgr.getConsumerQueue(Constants.Ctrl_UI_ClearSandRequest_QueueName).getTextNoWait();
			if (cmd != null && cmd.equalsIgnoreCase(Constants.Cmd_ClearSand_Request)) {
				MProducerQueue pq = mgr.getProducerQueue(Constants.Ctrl_UI_ClearSandResponse_QueueName);

				analyzeResult.clearPath();
				clientSendCount = 0;
				// analyzeResult.clearReportQueue();
				analyzeResult.clearReportMapById();

				// temp code !
				// ((FusionSerImpl_Emulation)
				// serMgr.getFusionSer()).resetCount();
				procDes = ProcDes.EmptyProcess();

				Util.log("Send clear response...");
				pq.send(Constants.Cmd_ClearSand_Response);
				Util.logln("Down.");
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public void receiveProcessCustomization() {
		try {
			ProcDes tpd = null;
			Object t = null;
			do {
				t = mgr.getConsumerQueue(Constants.Ctrl_UI_Proc_QueueName).getObjectNoWait();
				if (t != null) {
					tpd = (ProcDes) t;
				}
			} while (t != null);

			// Receive new process description, reset analyze status
			if (tpd != null) {// && !procDes.equals(tpd)) {
				analyzeResult.clearPath();
				// temp code !
				// ((FusionSerImpl_Emulation)
				// serMgr.getFusionSer()).resetCount();

				SerResourceList srl;
				srl = serMgr.connectService(tpd);
				if (srl != null) {
					procDes = tpd;
					MProducerQueue pq = mgr.getProducerQueue(Constants.Ctrl_UI_ProcConfirm_QueueName);
					Util.log("Send resource response to client... ");
					pq.send(srl);
					Util.logln("Down.");

				} else {
					procDes = ProcDes.EmptyProcess();
					// fail to get service
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public void detectAnalysis() {
		// analyzeResult.clearReportQueue();

		int[] types = procDes.detectorTypes;
		if (types != null && types.length != 0) {
			// ]]]]]]]]]]]
			HashMap<Integer,SensorAllData> sensorAllDataMap = serMgr.getSensorDataSer().queryAll();
			analyzeResult.setSensorAllDataMap(sensorAllDataMap);
			
			HashSet<Integer> typeset = new HashSet<Integer>();
			for (int i = 0; i < types.length; i++) {
				typeset.add(types[i]);
			}
			String tableName = null;
			int sensorType = -1;

			if (typeset.contains(Constants.TYPE_INFRARED_SENSOR)) {
				// Infrared
				tableName = Constants.Infrared_TableName;
				sensorType = Constants.TYPE_INFRARED_SENSOR;
				singleAnalyze(tableName, sensorType);
			}

			if (typeset.contains(Constants.TYPE_METAL_SENSOR)) {
				// Metal
				tableName = Constants.Metal_TableName;
				sensorType = Constants.TYPE_METAL_SENSOR;
				singleAnalyze(tableName, sensorType);
			}

			if (typeset.contains(Constants.TYPE_PRESSURE_SENSOR)) {
				// Pressure
				tableName = Constants.Pressure_TableName;
				sensorType = Constants.TYPE_PRESSURE_SENSOR;
				singleAnalyze(tableName, sensorType);
			}

			if (typeset.contains(Constants.TYPE_SOUND_SENSOR)) {
				// Sound
				soundAnalyze();
			}

			if (typeset.contains(Constants.TYPE_RFID_SENSOR)) {
				rfidAnalyze();
			}

		}
	}

	long t = System.currentTimeMillis();

	private void singleAnalyze(String tableName, int sensorType) {
		List<String> ids = Config.Singleton.Instance.getSensorIdListByType(sensorType);
		for (int i = 0; i < ids.size(); i++) {
			int sid = Integer.valueOf(ids.get(i));

			long time = System.currentTimeMillis();
			RowSensorData lastData = SensorAllData.parse(tableName, sid, analyzeResult.getSensorAllDataMap());
			// RowSensorData lastData = RowSensorData.Empty;
			// if (lastData.time.after(new Timestamp(System.currentTimeMillis()
			// - Config.QueryRange))) {
			// RowSensorData lastData = RowSensorData.Empty;
			// long now = System.currentTimeMillis();
			// Timestamp start = new Timestamp(now - Config.QueryRange);
			// Timestamp end = new Timestamp(now);
			// RowSensorData[] ds =
			// serMgr.getSensorDataSer().getValueBySensorAndTimeSegment(tableName,
			// 1, start, end);
			// if (ds != null) {
			// lastData = ds[0];
			// }

			DetectReport report = new DetectReport();
			report.detectorId = sid;
			report.dtctType = sensorType;
			report.lastValue = lastData.getvalue();
			report.timeStamp = lastData.time.getTime();
			DetectorInfo dinfo = Config.Singleton.Instance.getDetectorInfo(((Integer) sid).toString());
			report.x = dinfo.x;
			report.y = dinfo.y;
			report.x1 = dinfo.x1;
			report.y1 = dinfo.y1;
			report.threshold = dinfo.threshold;
			analyzeResult.addSensorPoint(sensorType, sid);

			HashMap<Integer, DetectReport> dmap = analyzeResult.getDMap();
			DetectReport dr = dmap.get(report.detectorId);
			if (sensorType == Constants.TYPE_INFRARED_SENSOR) {
				int value = (Integer) lastData.getvalue();
				if (dr == null || ((dr != null) && value > 0))
					analyzeResult.addReport(report);

			} else if (sensorType == Constants.TYPE_METAL_SENSOR) {
				int value = (Integer) lastData.getvalue();
				if (dr == null || ((dr != null) && value > 0))
					analyzeResult.addReport(report);

			} else if (sensorType == Constants.TYPE_PRESSURE_SENSOR) {
				int value = (Integer) lastData.getvalue();
				if (dr == null || ((dr != null) && value > dr.threshold))
					analyzeResult.addReport(report);

			} else {
				analyzeResult.addReport(report);
			}

			long currtime = System.currentTimeMillis();
			// for print
			int xx, yy;
			if (report.dtctType == Constants.TYPE_IDENTIFY) {
				xx = (int) ((double) (report.x + report.x1) / 2.0);
				yy = (int) ((double) (report.y + report.y1) / 2.0);
			} else {
				xx = report.x;
				yy = report.y;
			}
			Util.log("+++" + sid + " " + tableName + " " + lastData.value + " " + lastData.time.toGMTString() + " "
					+ " [sinlge query time] " + (currtime - time) + " (" + xx + "," + yy + ")");
			if (currtime - time > 3000) {
				Util.log("!!!!!!!!!!!!!!!!!!!!!!!!!!");
			}
			Util.logln("");
			// }

		}
	}

	private void rfidAnalyze() {
		String tableName = Constants.RFID_TableName;
		int sensorType = Constants.TYPE_RFID_SENSOR;
		List<String> ids = Config.Singleton.Instance.getSensorIdListByType(sensorType);
		for (int i = 0; i < ids.size(); i++) {
			int sid = Integer.valueOf(ids.get(0));
			RowSensorData lastData = serMgr.getSensorDataSer().getBySensorCompact(tableName, 1);
			if (lastData != RowSensorData.Empty) {
				if (analyzeResult.getLastRFID() == null) {
					DetectReport report = new DetectReport();
					report.detectorId = sid;
					report.dtctType = sensorType;
					report.lastValue = lastData.getvalue() + "#" + Constants.RFID_DES_OUR_TANK;
					report.timeStamp = System.currentTimeMillis();
					analyzeResult.setLastRFID(report);
					analyzeResult.addReport(report);
					analyzeResult.addSensorPoint(sensorType, sid);
					Util.logln("+++" + sid + " " + tableName + " " + lastData.value + " " + report.timeStamp + " ");
				}
			} else {
				analyzeResult.setLastRFID(null);
			}
		}
	}

	private void soundAnalyze() {
		String tableName = Constants.Sound_TableName;
		int sensorType = Constants.TYPE_SOUND_SENSOR;
		List<String> ids = Config.Singleton.Instance.getSensorIdListByType(sensorType);

		RowSensorData lastData = serMgr.getSensorDataSer().getBySensorCompact(tableName, 34);
		for (int i = 0; i < ids.size(); i++) {
			int sid = Integer.valueOf(ids.get(0));
			if (lastData != RowSensorData.Empty) {
				DetectReport report = new DetectReport();
				report.detectorId = sid;
				report.dtctType = sensorType;
				report.lastValue = lastData.getvalue();
				report.timeStamp = System.currentTimeMillis();
				analyzeResult.addReport(report);
				analyzeResult.addSensorPoint(sensorType, sid);
				Util.logln("+++" + sid + " " + tableName + " " + lastData.value + " " + report.timeStamp + " ");
			}
		}
	}

	private void fusionAnalyze() {
		int[] types = procDes.detectorTypes;
		if (types != null && types.length != 0) {

			HashMap<Integer, ReportQueue> fusionTask = new HashMap<Integer, ReportQueue>();
			ArrayList<HashMap<Integer, ReportQueue>> singleFusionTaskList = new ArrayList<HashMap<Integer, ReportQueue>>();
			// Typeset
			for (int i = 0; i < types.length; i++) {
				if (types[i] != Constants.TYPE_TRACK && types[i] != Constants.TYPE_RFID_SENSOR) {
					List<String> islist = Config.Singleton.Instance.getSensorIdListByType(types[i]);
					HashMap<Integer, ReportQueue> singleFusionTask = new HashMap<Integer, ReportQueue>();
					for (String sid : islist) {
						int id = Integer.valueOf(sid);
						HashMap<Integer, ReportQueue> anarmap = analyzeResult.getDtctRprtMap();
						ReportQueue rq = anarmap.get(id);
						if (rq.length() > 0) {
							fusionTask.put(id, rq);
						}
						singleFusionTask.put(id, rq);
					}
					singleFusionTaskList.add(singleFusionTask);
				}
			}

			if (procDes.needFusion()) {
				FusionResult result = serMgr.getFusionSer().fuseAll(fusionTask);
				if (result != null && result.x > 2 && result.y > 2) {
					RegionPoint point = new RegionPoint();
					point.x = result.x;
					point.y = result.y;
					point.radius = result.radius;
					point.objType = result.objType;
					point.sensorType = result.sensorType;
					point.objType = result.objType;
					analyzeResult.addFusionPoint(point);
				}
			} else {
				for (HashMap<Integer, ReportQueue> singlefusiontask : singleFusionTaskList) {
					FusionResult result = serMgr.getFusionSer().fuseAll(singlefusiontask);
					if (result != null && result.x > 0 && result.y > 0) {
						RegionPoint point = new RegionPoint();
						point.x = result.x;
						point.y = result.y;
						point.radius = result.radius;
						point.sensorType = result.sensorType;
						point.objType = result.objType;
						analyzeResult.addSinglePathPoint(point);
					}
				}
			}

			// Typeset
			// for (int i = 0; i < types.length; i++) {
			// if (types[i] != Constants.TYPE_TRACK && types[i] !=
			// Constants.TYPE_RFID_SENSOR) {
			// ArrayList<FusionSensorData> typedatalist =
			// fusiontypeset.get(types[i]);
			// if (typedatalist == null) {
			// fusiontypeset.put(types[i], new
			// ArrayList<FusionSensorData>(120));
			// } else {
			// typedatalist.clear();
			// }
			// }
			// }
			//
			// ArrayList<FusionSensorData> fsds = new
			// ArrayList<FusionSensorData>();

			// DetectReport[] reports = analyzeResult.getReports();
			// for (int i = 0; i < reports.length; i++) {
			// FusionSensorData fsd = new FusionSensorData();
			// DetectReport r = reports[i];
			// Integer type = r.dtctType;
			// if (typeset.containsKey(type)) {
			// fsd.id = ((Integer) (r.detectorId)).toString();
			// fsd.type = r.dtctType;
			// fsd.lastValue = r.lastValue;
			// Config cfg = Config.Singleton.Instance;
			// DetectorInfo dinfo = cfg.getDetectorInfo(fsd.id);
			// fsd.x = dinfo.x;
			// fsd.y = dinfo.y;
			// fsd.x1 = dinfo.x1;
			// fsd.y1 = dinfo.y1;
			// fsds.add(fsd);
			// typeset.get(type).add(fsd);
			// }
			// }
			//
			// if (procDes.needFusion()) {
			// FusionSensorData[] farr = new FusionSensorData[fsds.size()];
			// FusionResult result =
			// serMgr.getFusionSer().fuse(fsds.toArray(farr));
			// if (result != null) {
			// RegionPoint point = new RegionPoint();
			// point.x = result.x;
			// point.y = result.y;
			// point.radius = result.radius;
			// point.objType = result.objType;
			// point.sensorType = result.sensorType;
			// point.objType = result.objType;
			// analyzeResult.addFusionPoint(point);
			// }
			// } else {
			// for (Iterator<Map.Entry<Integer, ArrayList<FusionSensorData>>> it
			// =
			// fusiontypeset.entrySet().iterator(); it
			// .hasNext();) {
			// ArrayList<FusionSensorData> fss = it.next().getValue();
			// FusionSensorData[] fs = new FusionSensorData[fss.size()];
			// FusionResult result =
			// serMgr.getFusionSer().fuseSingle(fss.toArray(fs));
			// if (result != null) {
			// RegionPoint point = new RegionPoint();
			// point.x = result.x;
			// point.y = result.y;
			// point.radius = result.radius;
			// point.sensorType = result.sensorType;
			// point.objType = result.objType;
			// analyzeResult.addSinglePathPoint(point);
			// }
			// }
			// }
		}
	}

	// temp code
	int ssss = 0;

	private void sendBattlefieldToClient() {
		try {
			Util.logln("Send battlefield coordination to client.");
			ArrayList<DetectorInfo> dl = Config.Singleton.Instance.getSersorInfoList();
			SubjectDescription[] sds = new SubjectDescription[dl.size() + 1];
			SubjectDescription sd0 = new SubjectDescription("0", Constants.TYPE_UNKNOWN, Constants.Battlefield_Width,
					Constants.Battlefield_Height, 0, 0);
			sds[0] = sd0;
			for (int i = 0; i < dl.size(); i++) {
				DetectorInfo di = dl.get(i);
				SubjectDescription sd = new SubjectDescription(di.id, di.type, di.x, di.y, di.x1, di.y1);
				sd.threshold = di.threshold;
				sds[i + 1] = sd;
			}
			mgr.getProducerQueue(Constants.Ctrl_UI_Battlefield_QueueName).send(sds);

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private void sendServiceProfileToClient() {
		try {
			Util.logln("Send service profiles coordination to client.");
			HashMap<Integer, SerResource> hash = Config.Singleton.Instance.getSerRscMapByType();
			SerResourceList srl = new SerResourceList(hash);
			mgr.getProducerQueue(Constants.Ctrl_UI_ServiceProfile_QueueName).send(srl);

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private void sendSensorDataToClient() {
		try {
			HashMap<Integer, DetectReport> dmap = analyzeResult.getDMap();
			Iterator<Map.Entry<Integer, DetectReport>> it = dmap.entrySet().iterator();
			while (it.hasNext()) {
				DetectReport dr = it.next().getValue();
				if (dr != null) {
					mgr.getProducerQueue(Constants.Ctrl_UI_Sensor_QueueName).send(dr);
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	private void sendPathToClient() {
		try {
			int[] tys = procDes.detectorTypes;
			Util.logln("send fusion path to client. (" + procDes.needFusion() + " ");
			if (tys != null) {
				for (int i = 0; i < tys.length; i++)
					System.out.print(tys[i] + " ");
			}

			MProducerQueue q = mgr.getProducerQueue(Constants.Ctrl_UI_Path_QueueName);
			if (procDes.needFusion() && !analyzeResult.getFusionPath().isEmpty()) {
				RegionPath path = analyzeResult.getFusionPath();
				q.send(path);
				Util.logln("<<<Send_Path (" + path.getLast().x + "," + path.getLast().y + ":" + path.getLast().radius
						+ ") sensor_type=" + path.getLast().sensorType + "  object_type=" + path.getLast().objType);
			} else {
				int[] types = procDes.detectorTypes;
				if (types != null) {
					for (int i = 0; i < types.length; i++) {
						if (types[i] != Constants.TYPE_TRACK) {
							RegionPath path = analyzeResult.getSinglePathSet().get(types[i]);
							if (path != null && !path.isEmpty()) {
								q.send(path);
								Util.logln("<<<<Send_Path (" + path.getLast().x + "," + path.getLast().y + ":"
										+ path.getLast().radius + ") sensor_type=" + path.getLast().sensorType
										+ "  object_type=" + path.getLast().objType);
							}
						}
					}
				}
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	
	

	// private void sendToProcessController(AnalyzeResult rslt) {
	// try {
	//
	// MConsumerQueue pq =
	// mgr.getConsumerQueue(Constants.ListenAvailable_Process_Monitor_QueueName);
	// String b = pq.getTextNoWait();
	//
	// if (b != null) {
	// isProcessAvailable = b;
	// }
	//
	// if (isProcessAvailable.equalsIgnoreCase(Constants.ProcessAvailable_True))
	// {
	// System.out.println(rslt.getDtctRprt().detectorId);
	// mgr.getProducerQueue(Constants.Smoke_Monitor_Process_QueueName).send(rslt.getDtctRprt());
	// }
	//
	// } catch (Exception ex) {
	// ex.printStackTrace();
	// }
	// }

	public static void main(String[] args) {
		try {
			CombinedDetectionMonitor cdm = new CombinedDetectionMonitor();
			cdm.run();

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public static void f(Double d) {

	}
}
